/** @file

  Copyright (c) 2004  - 2020, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "PlatformSetupDxe.h"
#include "Guid/SetupVariable.h"
#include <Protocol/FormBrowserEx2.h>


#define EFI_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('C', 'l', 'b', 'k')
#define EFI_CALLBACK_INFO_FROM_THIS(a)  CR (a, EFI_CALLBACK_INFO, ConfigAccess, EFI_CALLBACK_INFO_SIGNATURE)

typedef struct {
  UINTN                           Signature;
  EFI_HANDLE                      DriverHandle;
  EFI_HII_HANDLE                  RegisteredHandle;
  SYSTEM_CONFIGURATION            FakeNvData;
  SYSTEM_CONFIGURATION            BackupNvData;
  EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting;
  EFI_HII_CONFIG_ACCESS_PROTOCOL  ConfigAccess;
} EFI_CALLBACK_INFO;

#pragma pack(1)

//
// HII specific Vendor Device Path definition.
//
typedef struct {
  VENDOR_DEVICE_PATH             VendorDevicePath;
  EFI_DEVICE_PATH_PROTOCOL       End;
} HII_VENDOR_DEVICE_PATH;

#pragma pack()

//
// uni string and Vfr Binary data.
//
extern UINT8  VfrBin[];
extern UINT8  PlatformSetupDxeStrings[];

EFI_HANDLE            mImageHandle;

//
// module global data
//
#define EFI_NORMAL_SETUP_GUID \
  { 0xec87d643, 0xeba4, 0x4bb5, 0xa1, 0xe5, 0x3f, 0x3e, 0x36, 0xb2, 0xd, 0xa9 }

EFI_GUID                     mNormalSetupGuid = EFI_NORMAL_SETUP_GUID;

EFI_GUID                     mSystemConfigGuid = SYSTEM_CONFIGURATION_GUID;
CHAR16                       mVariableName[] = L"Setup";
CHAR16                       mSetupName[] = L"Setup";
EFI_CALLBACK_INFO           *mCallbackInfo;
BOOLEAN                      GlobalReset=FALSE;

HII_VENDOR_DEVICE_PATH  mHiiVendorDevicePath = {
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    EFI_CALLER_ID_GUID
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      (UINT8) (END_DEVICE_PATH_LENGTH),
      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
    }
  }
};

/**
  This function allows a caller to extract the current configuration for one
  or more named elements from the target driver.

  @param  This         Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param  Request      A null-terminated Unicode string in <ConfigRequest> format.
  @param  Progress     On return, points to a character in the Request string.
                       Points to the string's null terminator if request was successful.
                       Points to the most recent '&' before the first failing name/value
                       pair (or the beginning of the string if the failure is in the
                       first name/value pair) if the request was not successful.
  @param  Results      A null-terminated Unicode string in <ConfigAltResp> format which
                       has all values filled in for the names in the Request string.
                       String to be allocated by the called function.

  @retval EFI_SUCCESS            The Results is filled with the requested values.
  @retval EFI_OUT_OF_RESOURCES   Not enough memory to store the results.
  @retval EFI_INVALID_PARAMETER  Request is NULL, illegal syntax, or unknown name.
  @retval EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.

**/

VOID
CheckSystemConfigLoad(SYSTEM_CONFIGURATION *SystemConfigPtr);

VOID
CheckSystemConfigSave(SYSTEM_CONFIGURATION *SystemConfigPtr);

VOID
ConfirmSecureBootTest();

VOID
LoadLpssDefaultValues (
  IN EFI_CALLBACK_INFO                       *Private
  )
{
  //
  // Load LPSS and SCC default configurations for Android
  //
  Private->FakeNvData.LpsseMMCEnabled            = FALSE;
  Private->FakeNvData.LpssSdioEnabled            = TRUE;
  Private->FakeNvData.LpssSdcardEnabled          = TRUE;
  Private->FakeNvData.LpssSdCardSDR25Enabled     = FALSE;
  Private->FakeNvData.LpssSdCardDDR50Enabled     = TRUE;
  Private->FakeNvData.LpssMipiHsi                = FALSE;
  Private->FakeNvData.LpsseMMC45Enabled          = TRUE;
  Private->FakeNvData.LpsseMMC45DDR50Enabled     = TRUE;
  Private->FakeNvData.LpsseMMC45HS200Enabled     = FALSE;
  Private->FakeNvData.LpsseMMC45RetuneTimerValue = 8;
  Private->FakeNvData.eMMCBootMode               = 1;     // Auto Detect

  Private->FakeNvData.GOPEnable = TRUE;
  Private->FakeNvData.SecureBoot = TRUE;
  Private->FakeNvData.UsbAutoMode = TRUE;
  Private->FakeNvData.UsbXhciSupport = TRUE;
  Private->FakeNvData.PchUsb30Mode = TRUE;
  Private->FakeNvData.LegacyUSBBooting = FALSE;
  Private->FakeNvData.PchUsb20 = FALSE;
}


EFI_STATUS
EFIAPI
SystemConfigExtractConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Request,
  OUT EFI_STRING                             *Progress,
  OUT EFI_STRING                             *Results
  )
{
  SetupInfo();
  return EFI_UNSUPPORTED;
}

/**
  This function processes the results of changes in configuration.

  @param  This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param  Configuration   A null-terminated Unicode string in <ConfigRequest> format.
  @param  Progress        A pointer to a string filled in with the offset of the most
                          recent '&' before the first failing name/value pair (or the
                          beginning of the string if the failure is in the first
                          name/value pair) or the terminating NULL if all was successful.

  @retval EFI_SUCCESS            The Results is processed successfully.
  @retval EFI_INVALID_PARAMETER  Configuration is NULL.
  @retval EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.

**/
EFI_STATUS
EFIAPI
SystemConfigRouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
  )
{
  return EFI_UNSUPPORTED;
}

/**
  This is the function that is called to provide results data to the driver.  This data
  consists of a unique key which is used to identify what data is either being passed back
  or being asked for.

  @param  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
  @param  Action         A null-terminated Unicode string in <ConfigRequest> format.
  @param  KeyValue       A unique Goto OpCode callback value which record user's selection.
                         0x100 <= KeyValue <0x500 : user select a controller item in the first page;
                         KeyValue == 0x1234       : user select 'Refresh' in first page, or user select 'Go to Previous Menu' in second page
                         KeyValue == 0x1235       : user select 'Pci device filter' in first page
                         KeyValue == 0x1500       : user select 'order ... priority' item in second page
                         KeyValue == 0x1800       : user select 'commint changes' in third page
                         KeyValue == 0x2000       : user select 'Go to Previous Menu' in third page
  @param  Type           The type of value for the question.
  @param  Value          A pointer to the data being sent to the original exporting driver.
  @param  ActionRequest  On return, points to the action requested by the callback function.

  @retval EFI_SUCCESS    Always returned.

**/
EFI_STATUS
EFIAPI
SystemConfigCallback (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        KeyValue,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  )
{
  EFI_CALLBACK_INFO             *Private;
  SYSTEM_CONFIGURATION          *FakeNvData;
  SYSTEM_CONFIGURATION          *SetupData;
  UINTN                         SizeOfNvStore;
  EFI_INPUT_KEY                 Key;
  CHAR16                        *StringBuffer1;
  CHAR16                        *StringBuffer2;
  CHAR16                        *StringBuffer3;
  EFI_STATUS                    Status;
  UINTN                         DataSize;
  UINT8                         OsSelection;
  EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;

  StringBuffer1 = AllocateZeroPool (200 * sizeof (CHAR16));
  ASSERT (StringBuffer1 != NULL);
  StringBuffer2 = AllocateZeroPool (200 * sizeof (CHAR16));
  ASSERT (StringBuffer2 != NULL);
  StringBuffer3 = AllocateZeroPool (200 * sizeof (CHAR16));
  ASSERT (StringBuffer3 != NULL);

  switch (Action) {
  case EFI_BROWSER_ACTION_CHANGING:
  {
    if (KeyValue == 0x1235) {
      StrCpyS (StringBuffer1, 200, L"Will you disable PTT ? ");
      StrCpyS (StringBuffer2, 200, L"Enter (YES)  /   Esc (NO)");

      //
      // Popup a menu to notice user
      //
      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the YES Response key,
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {

      }
    } else if (KeyValue == 0x1236) {
      StrCpyS (StringBuffer1, 200, L"Will you revoke trust ? ");
      StrCpyS (StringBuffer2, 200, L"Enter (YES)  /   Esc (NO)");

      //
      // Popup a menu to notice user
      //
      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the YES Response key,
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {

      }
	 } else if (KeyValue == 0x1239) {
	   if (Value->u8 == 0x00) {
       StrCpyS (StringBuffer1, 200, L"WARNING: SOC may be damaged due to high temperature");
       StrCpyS (StringBuffer2, 200, L"when DPTF is disabled and IGD turbo is enabled.");
       StrCpyS (StringBuffer3, 200, L"Press Enter/ESC to continue...");

        //
        // Popup a menu to notice user
        //
        do {
          CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, StringBuffer3, NULL);
        } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));
    }
    } else if (KeyValue == 0x1240) { // secure erase feature of eMMC
	    //
      // Popup a menu to notice user
      //
      StrCpyS (StringBuffer1, 200, L"WARNING: All your data on the eMMC will be lost");
      StrCpyS (StringBuffer2, 200, L"Do you really want to enable secure erase on eMMC?");
      StrCpyS (StringBuffer3, 200, L"       Enter (YES)    /    Esc (NO)        ");

      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, StringBuffer3,NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the ESC Response key,
      //
      if (Key.ScanCode == SCAN_ESC) {
        Private = EFI_CALLBACK_INFO_FROM_THIS (This);
        FakeNvData = &Private->FakeNvData;

        Status = HiiGetBrowserData (
		               &mSystemConfigGuid,
				           mVariableName,
				           sizeof (SYSTEM_CONFIGURATION),
				           (UINT8 *) FakeNvData
				           );
        if (!EFI_ERROR (Status)) {
             FakeNvData->SecureErase = 0;
             HiiSetBrowserData (
               &mSystemConfigGuid,
               mVariableName,
               sizeof (SYSTEM_CONFIGURATION),
               (UINT8 *) FakeNvData,
               NULL
               );
        }
        break;
      }

      //
      // If the user hits the YES Response key
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
        //
        // Save change
        //
        Private = EFI_CALLBACK_INFO_FROM_THIS (This);
        FakeNvData = &Private->FakeNvData;

        Status = HiiGetBrowserData (
		               &mSystemConfigGuid,
				           mVariableName,
				           sizeof (SYSTEM_CONFIGURATION),
				          (UINT8 *) FakeNvData
				          );
        if (!EFI_ERROR (Status)) {
          Status = gRT->SetVariable (
                          L"Setup",
                          &mNormalSetupGuid,
                          EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                          sizeof(SYSTEM_CONFIGURATION),
                          &Private->FakeNvData
                          );
        }

        //
        // Reset system
        //
        gRT->ResetSystem(
		           EfiResetCold,
			         EFI_SUCCESS,
			         0,
			         NULL
			         );

    }


    }
    else if (KeyValue == 0xF001) {
      //
      // Popup a menu to notice user
      //
      StrCpyS (StringBuffer1, 200, L"Do you want to Commit Changes and Exit?");
      StrCpyS (StringBuffer2, 200, L"         Enter (YES) / Esc (NO)        ");

      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the YES Response key
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
        //
        // Save change
        //
        Private = EFI_CALLBACK_INFO_FROM_THIS (This);
        FakeNvData = &Private->FakeNvData;

        Status = HiiGetBrowserData (
		           &mSystemConfigGuid,
				   mVariableName,
				   sizeof (SYSTEM_CONFIGURATION),
				   (UINT8 *) FakeNvData
				   );
        if (!EFI_ERROR (Status)) {
          Status = gRT->SetVariable (
                          L"Setup",
                          &mNormalSetupGuid,
                          EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                          sizeof(SYSTEM_CONFIGURATION),
                          &Private->FakeNvData
                          );
        }

		//
		// Update Secure Boot configuration changes
		//
        CheckSystemConfigSave(FakeNvData);

        //
        // Reset system
        //
        if (GlobalReset == TRUE) {
          //
          // Issue full reset
          //
          IoWrite8 (
            (UINTN) 0XCF9,
            (UINT8) 0x02
            );

          IoWrite8 (
            (UINTN) 0xCF9,
            (UINT8) 0x0E
            );
        } else {
        	gRT->ResetSystem(
			       EfiResetCold,
				   EFI_SUCCESS,
				   0,
				   NULL
				   );
        }
      }
    } else if (KeyValue == 0xF002) {
      //
      // Popup a menu to notice user
      //
      StrCpyS (StringBuffer1, 200, L"Do you want to Discard Changes and Exit?");
      StrCpyS (StringBuffer2, 200, L"         Enter (YES) / Esc (NO)         ");

      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the YES Response key
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
        //
        // Reset system
        //
        gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
      }
    } else if (KeyValue == 0xF003) {
      //
      // Popup a menu to notice user
      //
      StrCpyS (StringBuffer1, 200, L"Do you want to load setup defaults and Exit?");
      StrCpyS (StringBuffer2, 200, L"         Enter (YES) / Esc (NO)             ");

      do {
        CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, NULL);
      } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

      //
      // If the user hits the YES Response key
      //
      if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {

        Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2);
        FormBrowserEx2->ExecuteAction(BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD);

        FakeNvData = AllocateZeroPool (sizeof(SYSTEM_CONFIGURATION));

        if (FakeNvData == NULL) {
          return EFI_OUT_OF_RESOURCES;
        }
        
        Status = HiiGetBrowserData (
		           &mSystemConfigGuid,
				   mVariableName,
				   sizeof (SYSTEM_CONFIGURATION),
				   (UINT8 *) FakeNvData
				   );
        
        if (!EFI_ERROR (Status)) {
          Status = gRT->SetVariable (
                          L"Setup",
                          &mNormalSetupGuid,
                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                          sizeof(SYSTEM_CONFIGURATION),
                          FakeNvData
                          );
        }

        DataSize = sizeof(OsSelection);
        Status = gRT->GetVariable(
                        L"OsSelection",
                        &gOsSelectionVariableGuid,
                        NULL,
                        &DataSize,
                        &OsSelection
                        );

        if (EFI_ERROR(Status) || (OsSelection != FakeNvData->ReservedO)) {
          OsSelection = FakeNvData->ReservedO;
          Status = gRT->SetVariable (
                          L"OsSelection",
                          &gOsSelectionVariableGuid,
                          EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                          sizeof(OsSelection),
                          &OsSelection
                          );
        }

        FreePool (FakeNvData);

        //
        // Reset system
        //
        gRT->ResetSystem(
		       EfiResetCold,
			   EFI_SUCCESS,
			   0,
			   NULL
			   );
      }
    } else if ((KeyValue == 0x123A) || (KeyValue == 0x123B) || (KeyValue == 0x123C)) {
        StrCpyS (StringBuffer1, 200, L"WARNING: Enable or disable USB Controllers will ");
        StrCpyS (StringBuffer2, 200, L"make global reset to restart system.");
        StrCpyS (StringBuffer3, 200, L"Press Enter/ESC to continue...");
        //
        // Popup a menu to notice user
        //
        do {
          CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, StringBuffer1, StringBuffer2, StringBuffer3, NULL);
        } while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));

        FakeNvData = AllocateZeroPool (sizeof(SYSTEM_CONFIGURATION));
        Status = HiiGetBrowserData (
		           &mSystemConfigGuid,
				   mVariableName,
				   sizeof (SYSTEM_CONFIGURATION),
				   (UINT8 *) FakeNvData
				   );
        //
        // Get variable data
        //
        SizeOfNvStore = sizeof(SYSTEM_CONFIGURATION);
        SetupData = AllocateZeroPool (sizeof(SYSTEM_CONFIGURATION));
        Status = gRT->GetVariable(
                        L"Setup",
                        &mNormalSetupGuid,
                        NULL,
                        &SizeOfNvStore,
                        SetupData
                        );
        if ((SetupData->UsbAutoMode != FakeNvData->UsbAutoMode) ||
        	   (SetupData->UsbXhciSupport != FakeNvData->UsbXhciSupport) ||
        	   (SetupData->PchUsb20 != FakeNvData->PchUsb20)) {
          GlobalReset = TRUE;
        } else {
          GlobalReset = FALSE;
        }

    }
  }
  break;

  default:
    break;
  }

  FreePool (StringBuffer1);
  FreePool (StringBuffer2);
  FreePool (StringBuffer3);

  //
  // Workaround for Load Default for "DPTF Enable"
  //
  if (Action == EFI_BROWSER_ACTION_DEFAULT_STANDARD) {
    if (KeyValue == 0x1239) {
      return EFI_NOT_FOUND;
    }
  }

  if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {
    //
    // Do nothing for UEFI OPEN/CLOSE Action
    //
    return EFI_SUCCESS;
  }

  Private = EFI_CALLBACK_INFO_FROM_THIS (This);
  FakeNvData = &Private->FakeNvData;
  if (!HiiGetBrowserData (&mSystemConfigGuid, mVariableName, sizeof (SYSTEM_CONFIGURATION), (UINT8 *) FakeNvData)) {
    return EFI_NOT_FOUND;
  }

  if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) && (Private->FakeNvData.ReservedO != Private->BackupNvData.ReservedO)) {
    Private->BackupNvData.ReservedO = Private->FakeNvData.ReservedO;
    LoadLpssDefaultValues (Private);
  }

  //
  // When user selected the secure erase, set it to disable
  //
  if((KeyValue == 0x1240) && (Action == EFI_BROWSER_ACTION_CHANGED)) {
    FakeNvData->SecureErase = 0;
  }

  if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_CHANGED)) {
    //
    // If function 0 is disabled, function 1 ~ 7 also required to be disabled.
    //
    if (Private->FakeNvData.LpssDma0Enabled == 0) {
      Private->FakeNvData.LpssHsuart0Enabled = 0;
      Private->FakeNvData.LpssHsuart1Enabled = 0;
      Private->FakeNvData.LpssPwm0Enabled    = 0;
      Private->FakeNvData.LpssPwm1Enabled    = 0;
      Private->FakeNvData.LpssSpiEnabled     = 0;
    }


    //
    // If function 0 is disabled, function 1 ~ 7 also required to be disabled.
    //
    if (Private->FakeNvData.LpssDma1Enabled == 0) {
      Private->FakeNvData.LpssI2C0Enabled = 0;
      Private->FakeNvData.LpssI2C1Enabled = 0;
      Private->FakeNvData.LpssI2C2Enabled = 0;
      Private->FakeNvData.LpssI2C3Enabled = 0;
      Private->FakeNvData.LpssI2C4Enabled = 0;
      Private->FakeNvData.LpssI2C5Enabled = 0;
      Private->FakeNvData.LpssI2C6Enabled = 0;
    }
  }


  //
  // Pass changed uncommitted data back to Form Browser
  //
  HiiSetBrowserData (&mSystemConfigGuid, mVariableName, sizeof (SYSTEM_CONFIGURATION), (UINT8 *) FakeNvData, NULL);

  return EFI_SUCCESS;
}


/**
  The driver Entry Point. The function will export a disk device class formset and
  its callback function to hii database.

  @param  ImageHandle    The firmware allocated handle for the EFI image.
  @param  SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS    The entry point is executed successfully.
  @retval other          Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
PlatformSetupDxeInit (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS                  Status;
  EFI_FORM_BROWSER2_PROTOCOL  *FormBrowser2;

  mImageHandle = ImageHandle;

  //
  // There should only be one Form Configuration protocol
  //
  Status = gBS->LocateProtocol (
                 &gEfiFormBrowser2ProtocolGuid,
                 NULL,
                 (VOID **) &FormBrowser2
                 );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  mCallbackInfo = AllocateZeroPool (sizeof (EFI_CALLBACK_INFO));
  if (mCallbackInfo == NULL) {
    return EFI_BAD_BUFFER_SIZE;
  }

  mCallbackInfo->Signature = EFI_CALLBACK_INFO_SIGNATURE;
  mCallbackInfo->ConfigAccess.ExtractConfig = SystemConfigExtractConfig;
  mCallbackInfo->ConfigAccess.RouteConfig   = SystemConfigRouteConfig;
  mCallbackInfo->ConfigAccess.Callback      = SystemConfigCallback;

  //
  // Install Device Path Protocol and Config Access protocol to driver handle
  // Install Platform Driver Override Protocol to driver handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mCallbackInfo->DriverHandle,
                  &gEfiDevicePathProtocolGuid,
                  &mHiiVendorDevicePath,
                  &gEfiHiiConfigAccessProtocolGuid,
                  &mCallbackInfo->ConfigAccess,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    goto Finish;
  }

  //
  // Publish our HII data
  //
  mCallbackInfo->RegisteredHandle = HiiAddPackages (
                                      &mSystemConfigGuid,
                                      mCallbackInfo->DriverHandle,
                                      VfrBin,
                                      PlatformSetupDxeStrings,
                                      NULL
                                      );
  if (mCallbackInfo->RegisteredHandle == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Finish;
  }

  mHiiHandle = mCallbackInfo->RegisteredHandle;

  //
  // Locate ConfigRouting protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **) &mCallbackInfo->HiiConfigRouting
                  );
  if (EFI_ERROR (Status)) {
    goto Finish;
  }

  //
  // Clear all the globle variable
  //
  return EFI_SUCCESS;

Finish:
  if (mCallbackInfo->DriverHandle != NULL) {
    gBS->UninstallMultipleProtocolInterfaces (
           mCallbackInfo->DriverHandle,
           &gEfiDevicePathProtocolGuid,
           &mHiiVendorDevicePath,
           &gEfiHiiConfigAccessProtocolGuid,
           &mCallbackInfo->ConfigAccess,
           NULL
           );
  }

  if (mCallbackInfo->RegisteredHandle != NULL) {
    HiiRemovePackages (mCallbackInfo->RegisteredHandle);
  }

  if (mCallbackInfo != NULL) {
    FreePool (mCallbackInfo);
  }

  return Status;
}

/**
  Unload its installed protocol.

  @param[in]  ImageHandle       Handle that identifies the image to be unloaded.

  @retval EFI_SUCCESS           The image has been unloaded.
**/
EFI_STATUS
EFIAPI
PlatformSetupDxeUnload (
  IN EFI_HANDLE  ImageHandle
  )
{
  if (mCallbackInfo != NULL) {
    if (mCallbackInfo->DriverHandle != NULL) {
      gBS->UninstallMultipleProtocolInterfaces (
             mCallbackInfo->DriverHandle,
             &gEfiDevicePathProtocolGuid,
             &mHiiVendorDevicePath,
             &gEfiHiiConfigAccessProtocolGuid,
             &mCallbackInfo->ConfigAccess,
             NULL
             );
    }

    if (mCallbackInfo->RegisteredHandle != NULL) {
      HiiRemovePackages (mCallbackInfo->RegisteredHandle);
    }

    FreePool (mCallbackInfo);
  }

  return EFI_SUCCESS;
}

